Skip to content

Align public docs and website with the OCI release - #42

Merged
gitcommit90 merged 1 commit into
mainfrom
docs/oci-product-accuracy
Aug 1, 2026
Merged

Align public docs and website with the OCI release#42
gitcommit90 merged 1 commit into
mainfrom
docs/oci-product-accuracy

Conversation

@gitcommit90

@gitcommit90 gitcommit90 commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Outcome\n\nUpdates the README, standalone website, manual, user guide, vision, and changelog so the public product description matches the OCI architecture shipped in v0.0.30.\n\n## Corrections\n\n- describes Apple container machines, native Linux Podman containers, and Windows managed-WSL OCI containers accurately\n- scopes signing claims by platform and discloses v0.0.30 Windows as NotSigned\n- removes unavailable current mobile download/App Store claims\n- documents the current Captain-to-Skipper Photon flow and shipped Cowork capabilities\n- retires the stale pre-OCI demo claim and redirects demo host traffic to the product site\n- adds regression coverage for current Windows install copy and the retired demo host\n\n## Verification\n\n- npm run typecheck\n- npm run build\n- node --test test/site.mjs\n- npm test\n- git diff --check\n\nThe already-published v0.0.30 release ledger was also corrected in place: its Linux SHA-256 now matches the uploaded asset, and the literal RELEASES manifest is included. No tag or release asset changed.

Summary by CodeRabbit

  • Documentation

    • Updated platform availability, mobile access, installation, onboarding, and workspace guidance.
    • Added security and release verification details for macOS, Windows, and Linux.
    • Clarified private conversation behavior, self-hosting, container environments, and desktop connectivity.
    • Documented enhanced Code and presentation features, including HTML previews and improved deck layouts.
    • Clarified that the retired demo sandbox redirects to the main product site.
  • Bug Fixes

    • Added permanent redirects from the retired demo domain while preserving paths and query parameters.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR updates platform, mobile, security, workspace, runtime, and manual documentation. It adds a permanent redirect for the retired demo domain and updates site integration tests for redirect and Windows release-status behavior.

Changes

Documentation and runtime status

Layer / File(s) Summary
Platform and workspace documentation
README.md, docs/USER_GUIDE.md
Documentation now covers supported desktop hosts, browser-based mobile access, release verification, Cowork status and sessions, HTML previews, presentation behavior, and workspace hosting.
Runtime and manual content
CHANGELOG.md, docs/VISION.md, site/content.mjs, site/manual.html, site/story.html
Documentation now describes Photon access restrictions, OCI and Apple container isolation, platform security status, current product availability, and the retired sandbox.
Retired demo redirect and validation
site/server.mjs, test/site.mjs
The demo host returns a permanent redirect that preserves the path and query string. Tests validate the redirect and updated Windows release-status text.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • gitcommit90/1Helm#32: Related desktop/mobile workspace connection, Cowork, update-security, and retired-demo behavior.
  • gitcommit90/1Helm#40: Related OCI runtime architecture and container terminology.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the outcome, corrections, release-ledger update, and verification, but omits several required template sections and checklists. Add the required Summary, Type of change, Release notes, acceptance ledger, Verification checklist, and Post-merge sections.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: aligning public documentation and website content with the OCI release.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/oci-product-accuracy

Comment @coderabbitai help to get the list of available commands.

@gitcommit90
gitcommit90 merged commit 602d463 into main Aug 1, 2026
5 of 6 checks passed
@gitcommit90
gitcommit90 deleted the docs/oci-product-accuracy branch August 1, 2026 01:55

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@site/content.mjs`:
- Line 32: Update the introductory scope statement within installMac to describe
only the macOS install path, rather than implying Apple Silicon Macs are the
sole native consumer product target. Keep the platform-specific requirements and
installation instructions unchanged.
- Line 16: Update the gettingStarted onboarding content around the native mobile
apps sentence to state that v0.0.30 has no public mobile build and direct phone
and tablet users to the existing HTTPS browser interface. Keep the surrounding
installation and host-connection guidance unchanged.

In `@site/story.html`:
- Line 131: Update the caption paragraph around the “fully isolated from the
real machine” text to describe runtime-owned storage and configured CPU/memory
limits for OCI containers instead of claiming dedicated RAM, CPU, or VM-level
isolation. Preserve the distinction between the Mac Apple container machine and
Linux/Windows OCI container behavior, using the documented container boundary
wording.

In `@test/site.mjs`:
- Line 63: Update the negative assertion for gettingStarted to use word
boundaries around “signed Setup executable,” so it rejects only the explicit
phrase and not “unsigned Setup executable.”
- Around line 14-21: Update requestWithHost to apply a finite timeout via
req.setTimeout and res.setTimeout, with each timeout handler destroying the
connection and rejecting the promise. Add response error and aborted handlers
that reject as well, while preserving the existing successful end resolution.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d746075e-7732-46f0-b49b-15cbd8650689

📥 Commits

Reviewing files that changed from the base of the PR and between b8fffaf and ea8eaeb.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • README.md
  • docs/USER_GUIDE.md
  • docs/VISION.md
  • site/content.mjs
  • site/manual.html
  • site/server.mjs
  • site/story.html
  • test/site.mjs

Comment thread site/content.mjs
const security = doc("/manual/security-model", "Security model", "How 1Helm isolates residents, brokers credentials, audits actions, validates skills, and defines the human boundary.", `<p class="intro">Autonomy without architecture is just ambient authority. 1Helm makes routine action cheap inside a narrow world and makes boundary crossings explicit, attributable, and recoverable.</p><h2>Resident isolation</h2><p>Each ordinary channel receives a separate persistent Linux world: an Apple container machine with no Mac home mount, or a durable OCI container. Linux runs OCI natively. Windows hosts containers inside one managed WSL 2 runtime whose Windows-drive mounts and interop are disabled. Exact labels, storage mounts, and owner markers gate lifecycle operations. Other residents and the host home are not exposed.</p><h2>Authoritative files</h2><p>OCI workspace storage belongs to the runtime and is authoritative. Files and Cowork receive narrow direct access to that channel's storage; command and terminal paths do not copy the whole workspace. Apple's backend retains its bounded, symlink-contained mirror.</p><h2>Skipper boundary</h2><p>Skipper owns native host operations, fleet lifecycle, credential brokering, and cross-channel work. A resident calls Skipper directly with the invoking thread; a Captain-authored request is required for host-authorized operations. Skipper returns the result to the resident automatically.</p><h2>Credentials and connections</h2><p>Provider, Gmail, and Photon credentials stay in host-owned storage. Residents receive task-scoped tools and permission records, not raw access tokens or the native Messages database. Photon accepts only the configured Captain phone and keeps that direct Skipper conversation in the Captain's private <code>#main</code>.</p><h2>Skill supply chain</h2><p>The external catalog is discovery metadata, not executable trust. 1Helm shows the open registry's results without applying its own browse-time allowlist. A selected GitHub source is resolved to an immutable commit, bounded to 256 KiB, scanned for instruction override, exfiltration, remote-pipe execution, broad destructive commands, security disabling, private-host access, and prompt extraction, then hashed and wrapped beneath runtime authority.</p><h2>Audit and limits</h2><p>New activity, tool starts/results, and skill installation decisions enter an append-only SHA-256 chain. The chain is tamper-evident, not a remote transparency log: an administrator with database access can still delete or replace the entire database. Historical rows predating the chain are not backfilled.</p><h2>Known dependency debt</h2><p>The pinned Photon SDK currently carries moderate OpenTelemetry advisories upstream. It runs in a supervised loopback-only child process with telemetry disabled. 1Helm tracks the exact pin and will upgrade when the required Photon API remains compatible; this is not represented as a clean dependency audit.</p><h2>Report a vulnerability</h2><p>Use GitHub's private vulnerability reporting for the 1Helm repository. Do not open a public issue containing credentials, tokens, or an unpatched exploit.</p>`);

const gettingStarted = doc("/manual/getting-started", "Getting started", "Install 1Helm, connect providers, create the workspace, and give the first resident a real outcome.", `<p class="intro">The normal setup is three product decisions. 1Helm handles the infrastructure around them.</p><h2>1. Install</h2><p>On Apple Silicon, download the signed, notarized, and stapled DMG. On Windows 11 x64, download the signed Setup executable. Ubuntu/Debian hosts use the verified Linux systemd installer. Each platform may request one host-level approval for its isolated Linux runtime.</p><h2>2. Captain</h2><p>Create the first account. This is the Captain: owner, final authority, and administrator. Public registration closes after the Captain exists.</p><h2>3. Providers</h2><p>Connect one or more subscription accounts or API keys. You can add more later, pool accounts, select exact models, and build fallback or round-robin routes. There is no required single “AI brain.”</p><h2>4. Workspace</h2><p>Name the workspace. Terminals default on. 1Helm creates <code>#main</code> with the one Skipper, then you create ordinary channels with plain-language purposes. Every ordinary channel gets a private Linux computer.</p><h2>5. Give an outcome</h2><p>Try: <em>“Audit this launch folder, turn the notes into a decision brief, resolve obvious gaps yourself, and give me the finished PDF with evidence.”</em> The resident should inspect, execute, create the artifact, and call Skipper itself if it crosses the channel boundary.</p>`);
const gettingStarted = doc("/manual/getting-started", "Getting started", "Install 1Helm, connect providers, create the workspace, and give the first resident a real outcome.", `<p class="intro">The normal setup is three product decisions. 1Helm handles the infrastructure around them.</p><h2>1. Install or connect</h2><p>On Apple Silicon, download the signed, notarized, and stapled DMG. On Windows 11 x64, download the Setup executable; its Authenticode status is disclosed in the release notes and v0.0.30 is <code>NotSigned</code>. Ubuntu/Debian hosts use the digest-verified Linux systemd installer. A new desktop installation can host its own workspace or connect to an existing HTTPS 1Helm host. Native mobile apps connect only to an existing configured host. Starting a new host may request one administrator approval for its isolated Linux runtime.</p><h2>2. Captain</h2><p>Create the first account. This is the Captain: owner, final authority, and administrator. Public registration closes after the Captain exists.</p><h2>3. Providers</h2><p>Connect one or more subscription accounts or API keys. You can add more later, pool accounts, select exact models, and build fallback or round-robin routes. There is no required single “AI brain.”</p><h2>4. Workspace</h2><p>Name the workspace. Terminals default on. 1Helm creates <code>#main</code> with the one Skipper, then you create ordinary channels with plain-language purposes. Every ordinary channel gets a private Linux computer.</p><h2>5. Give an outcome</h2><p>Try: <em>“Audit this launch folder, turn the notes into a decision brief, resolve obvious gaps yourself, and give me the finished PDF with evidence.”</em> The resident should inspect, execute, create the artifact, and call Skipper itself if it crosses the channel boundary.</p>`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Disclose the current mobile build status in website onboarding.

Line 16 describes native mobile apps without stating that v0.0.30 has no public mobile build. The README, vision, and changelog state that the HTTPS browser interface is the current phone and tablet path. Users can otherwise search for an unavailable app.

State the no-public-build status and recommend the HTTPS browser interface.

Suggested wording
- Native mobile apps connect only to an existing configured host.
+ v0.0.30 has no public native mobile build. Use the HTTPS browser interface to connect to an existing configured host.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const gettingStarted = doc("/manual/getting-started", "Getting started", "Install 1Helm, connect providers, create the workspace, and give the first resident a real outcome.", `<p class="intro">The normal setup is three product decisions. 1Helm handles the infrastructure around them.</p><h2>1. Install or connect</h2><p>On Apple Silicon, download the signed, notarized, and stapled DMG. On Windows 11 x64, download the Setup executable; its Authenticode status is disclosed in the release notes and v0.0.30 is <code>NotSigned</code>. Ubuntu/Debian hosts use the digest-verified Linux systemd installer. A new desktop installation can host its own workspace or connect to an existing HTTPS 1Helm host. Native mobile apps connect only to an existing configured host. Starting a new host may request one administrator approval for its isolated Linux runtime.</p><h2>2. Captain</h2><p>Create the first account. This is the Captain: owner, final authority, and administrator. Public registration closes after the Captain exists.</p><h2>3. Providers</h2><p>Connect one or more subscription accounts or API keys. You can add more later, pool accounts, select exact models, and build fallback or round-robin routes. There is no required single “AI brain.”</p><h2>4. Workspace</h2><p>Name the workspace. Terminals default on. 1Helm creates <code>#main</code> with the one Skipper, then you create ordinary channels with plain-language purposes. Every ordinary channel gets a private Linux computer.</p><h2>5. Give an outcome</h2><p>Try: <em>“Audit this launch folder, turn the notes into a decision brief, resolve obvious gaps yourself, and give me the finished PDF with evidence.”</em> The resident should inspect, execute, create the artifact, and call Skipper itself if it crosses the channel boundary.</p>`);
const gettingStarted = doc("/manual/getting-started", "Getting started", "Install 1Helm, connect providers, create the workspace, and give the first resident a real outcome.", `<p class="intro">The normal setup is three product decisions. 1Helm handles the infrastructure around them.</p><h2>1. Install or connect</h2><p>On Apple Silicon, download the signed, notarized, and stapled DMG. On Windows 11 x64, download the Setup executable; its Authenticode status is disclosed in the release notes and v0.0.30 is <code>NotSigned</code>. Ubuntu/Debian hosts use the digest-verified Linux systemd installer. A new desktop installation can host its own workspace or connect to an existing HTTPS 1Helm host. v0.0.30 has no public native mobile build. Use the HTTPS browser interface to connect to an existing configured host. Starting a new host may request one administrator approval for its isolated Linux runtime.</p><h2>2. Captain</h2><p>Create the first account. This is the Captain: owner, final authority, and administrator. Public registration closes after the Captain exists.</p><h2>3. Providers</h2><p>Connect one or more subscription accounts or API keys. You can add more later, pool accounts, select exact models, and build fallback or round-robin routes. There is no required single “AI brain.”</p><h2>4. Workspace</h2><p>Name the workspace. Terminals default on. 1Helm creates <code>`#main`</code> with the one Skipper, then you create ordinary channels with plain-language purposes. Every ordinary channel gets a private Linux computer.</p><h2>5. Give an outcome</h2><p>Try: <em>“Audit this launch folder, turn the notes into a decision brief, resolve obvious gaps yourself, and give me the finished PDF with evidence.”</em> The resident should inspect, execute, create the artifact, and call Skipper itself if it crosses the channel boundary.</p>`);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@site/content.mjs` at line 16, Update the gettingStarted onboarding content
around the native mobile apps sentence to state that v0.0.30 has no public
mobile build and direct phone and tablet users to the existing HTTPS browser
interface. Keep the surrounding installation and host-connection guidance
unchanged.

Comment thread site/content.mjs
const connections = doc("/manual/connections", "Connections", "How Gmail, Photon/iMessage, and future host brokers expose narrow capabilities without leaking credentials or personal data.", `<p class="intro">A connection is a host-owned broker, not a secret copied into every resident's shell.</p><h2>Gmail</h2><p>When Gmail OAuth accounts exist on the 1Helm host, Captain-authorized Skipper can grant a resident access to named accounts. Current resident operations are account listing, search, message retrieval, and draft creation. Sending is disabled by default.</p><h2>Photon and iMessage</h2><p>The connector uses Photon's device-code login and Dashboard/Spectrum APIs to create or reuse a 1Helm project, rotate its one-time secret, register the operator, discover the assigned line, and start a long-lived <code>spectrum-ts</code> gRPC stream in a supervised loopback sidecar.</p><p>Inbound senders must match a channel mapping allowlist. Messages become real resident threads. The resident can reply in that already-authorized conversation; new outbound destinations require Skipper's explicit capability grant. Credentials never enter the resident computer.</p><p><strong>Current limitation:</strong> the reliable contract is text. Attachment events are represented conservatively and full attachment fidelity is still being verified.</p><h2>Connector standard</h2><p>New native connections must provide least-privilege scoping, secret isolation, reconnect/recovery, deduplication, an audit trail, deterministic tests, and honest capability status. A prompt saying “use Slack” is not a connector.</p>`);
const connections = doc("/manual/connections", "Connections", "How Gmail, Photon/iMessage, and future host brokers expose narrow capabilities without leaking credentials or personal data.", `<p class="intro">A connection is a host-owned broker, not a secret copied into every resident's shell.</p><h2>Gmail</h2><p>When Gmail OAuth accounts exist on the 1Helm host, Captain-authorized Skipper can grant a resident access to named accounts. Current resident operations are account listing, search, message retrieval, and draft creation. Sending is disabled by default.</p><h2>Photon and iMessage</h2><p>The connector uses Photon's device-code login and Dashboard/Spectrum APIs to create or reuse a 1Helm project, rotate its one-time secret, register the operator, discover the assigned line, and start a long-lived <code>spectrum-ts</code> gRPC stream in a supervised loopback sidecar.</p><p>Only the configured Captain phone is accepted. Its first inbound text opens a durable conversation with Skipper in the Captain's private <code>#main</code> Texts tab; replies return to that same phone conversation. The context survives connector restarts and Photon space-ID changes until the Captain sends <code>/new</code>. The Captain can continue any saved Texts thread on desktop, and desktop-only turns are not replayed as iMessages. Credentials never enter a resident computer.</p><p><strong>Current limitation:</strong> the reliable contract is text. Attachment events are represented conservatively and full attachment fidelity is still being verified.</p><h2>Connector standard</h2><p>New native connections must provide least-privilege scoping, secret isolation, reconnect/recovery, deduplication, an audit trail, deterministic tests, and honest capability status. A prompt saying “use Slack” is not a connector.</p>`);

const installMac = doc("/manual/install-macos", "Install on macOS", "Install the signed, notarized Apple Silicon 1Helm app and initialize per-channel Linux computers.", `<p class="intro">The native consumer product currently targets Apple Silicon Macs.</p><h2>Requirements</h2><ul><li>Apple Silicon Mac (<code>arm64</code>).</li><li>macOS 26 for Apple's container runtime.</li><li>Administrator approval once during verified runtime installation.</li></ul><h2>Install</h2><ol><li><a href="/download/macos">Download the current DMG</a>.</li><li>Open it and drag 1Helm to Applications.</li><li>Open 1Helm. Gatekeeper verifies the Developer ID signature and notarization ticket.</li><li>Complete Captain → Providers → Workspace. Approve Apple's signed runtime inline if requested.</li></ol><h2>Data and upgrades</h2><p>This generation stores application state under <code>~/Library/Application Support/1Helm-OCI-v1</code>. Profile → Check for updates asks the Mac hosting 1Helm to download and verify the signed, notarized update. When the host reports it ready, Restart &amp; install quiesces the local service and replaces the app. The browser is never given a DMG as the update action, and Application Support remains in place.</p><h2>Removal</h2><p>Use Settings → Admin → Prepare to remove 1Helm before trashing the app. This removes only verified 1Helm-owned channel machines while preserving the application state for a future reinstall.</p><div class="button-row">${button("/download/macos", "Download current DMG", "primary")}${button("https://github.com/gitcommit90/1Helm/releases", "Release history ↗")}</div>`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Correct the macOS product scope statement.

Line 32 says that the native consumer product targets Apple Silicon Macs. The same module documents supported Linux and Windows host products, and the manual states that all three platforms run the product. Limit this sentence to the macOS install path or list all supported host products.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@site/content.mjs` at line 32, Update the introductory scope statement within
installMac to describe only the macOS install path, rather than implying Apple
Silicon Macs are the sole native consumer product target. Keep the
platform-specific requirements and installation instructions unchanged.

Comment thread site/story.html
</div>
</div>
<p class="caption">Skipper builds it a virtual machine — a full computer with its own storage, RAM, and CPU, living inside yours, <span class="b">fully isolated from the real machine</span>. Its own files. Its own memory. <span class="b">Real memory.</span></p>
<p class="caption">Skipper builds it a private Linux computer — an Apple container machine on Mac, or its own OCI container on Linux and Windows — with its own storage, RAM, and CPU, <span class="b">fully isolated from the real machine</span>. Its own files. Its own memory. <span class="b">Real memory.</span></p>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Use container-accurate isolation wording.

Line 131 says that Linux and Windows OCI residents have their own RAM and CPU and are fully isolated from the real machine. The documented contract specifies runtime-owned storage and resource limits, not dedicated resources or VM-level isolation. This wording can create an incorrect security expectation.

Describe the runtime-owned storage and CPU/memory limits. State the documented container boundary instead of claiming full machine isolation.

Suggested wording
- with its own storage, RAM, and CPU, <span class="b">fully isolated from the real machine</span>
+ with runtime-owned storage and bounded CPU and memory, separated from the real machine by the documented runtime boundary
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<p class="caption">Skipper builds it a private Linux computer — an Apple container machine on Mac, or its own OCI container on Linux and Windows — with its own storage, RAM, and CPU, <span class="b">fully isolated from the real machine</span>. Its own files. Its own memory. <span class="b">Real memory.</span></p>
<p class="caption">Skipper builds it a private Linux computer — an Apple container machine on Mac, or its own OCI container on Linux and Windows — with runtime-owned storage and bounded CPU and memory, separated from the real machine by the documented runtime boundary. Its own files. Its own memory. <span class="b">Real memory.</span></p>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@site/story.html` at line 131, Update the caption paragraph around the “fully
isolated from the real machine” text to describe runtime-owned storage and
configured CPU/memory limits for OCI containers instead of claiming dedicated
RAM, CPU, or VM-level isolation. Preserve the distinction between the Mac Apple
container machine and Linux/Windows OCI container behavior, using the documented
container boundary wording.

Comment thread test/site.mjs
Comment on lines +14 to +21
const requestWithHost = (port, path, host) => new Promise((resolve, reject) => {
const req = request({ hostname: "127.0.0.1", port, path, headers: { host } }, (res) => {
res.resume();
res.once("end", () => resolve(res));
});
req.once("error", reject);
req.end();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- candidate test file ---'
cat -n test/site.mjs | sed -n '1,90p'

printf '%s\n' '--- Node.js target declarations ---'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
  '"(engines|node)"|node-version|NODE_VERSION|Node\.js|nodejs' \
  package.json package-lock.json npm-shrinkwrap.json .nvmrc .node-version \
  .github README.md 2>/dev/null || true

printf '%s\n' '--- requestWithHost usages ---'
rg -n --hidden --glob '!node_modules' 'requestWithHost|setTimeout|setTimeouts|timeout|aborted|res\.once\("error"|res\.on\("error"' .

Repository: gitcommit90/1Helm

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- project manifest ---'
python3 - <<'PY'
import json
with open("package.json") as f:
    package = json.load(f)
print(json.dumps({
    "engines": package.get("engines"),
    "scripts": package.get("scripts"),
    "dependencies": package.get("dependencies"),
    "devDependencies": package.get("devDependencies"),
}, indent=2))
PY

printf '%s\n' '--- CI runtime selection ---'
cat -n .github/workflows/ci.yml | sed -n '15,32p'

printf '%s\n' '--- focused timeout patterns ---'
rg -n -C 2 --hidden --glob '!node_modules' --glob '!package-lock.json' \
  'request\(|\.setTimeout\(|timeout\s*:' test scripts site .github package.json 2>/dev/null | head -200

printf '%s\n' '--- available local Node runtime ---'
node --version
node -e 'const {request}=require("node:http"); const req=request({hostname:"127.0.0.1",port:1}); console.log({requestSetTimeout:typeof req.setTimeout, requestSetTimeoutArity:req.setTimeout.length}); req.destroy();'

Repository: gitcommit90/1Helm

Length of output: 24737


🌐 Web query:

Node.js 22 http.ClientRequest setTimeout IncomingMessage aborted error official documentation

💡 Result:

In Node.js 22, the http.ClientRequest timeout behavior and IncomingMessage error handling are defined as follows: 1. http.ClientRequest setTimeout: When you use request.setTimeout(timeout[, callback]) on an http.ClientRequest, it sets a timeout on the underlying socket once it is assigned and connected [1][2]. The 'timeout' event is emitted when the socket experiences inactivity for the specified duration [1][2]. Crucially, this event only notifies you that the socket has been idle; it does not automatically destroy the request [1][2]. You must manually destroy the request (e.g., by calling request.destroy or request.abort) inside the 'timeout' event handler if you wish to terminate it [1][2]. If no 'timeout' listener is added to the request, response, or server, Node.js will automatically destroy the socket when it times out [2]. 2. IncomingMessage 'aborted' error: The 'aborted' event on IncomingMessage is deprecated [3]. Official documentation advises developers to listen for the 'close' event instead [3]. However, there is significant community discussion and known difficulty regarding this transition, as 'close' is emitted under various conditions (including when a request completes normally) [4][3], whereas 'aborted' specifically indicated a premature termination [4]. To accurately detect an abort in modern Node.js versions, some developers check the readableEnded property on the request/response object within the 'close' event handler to determine if the stream ended prematurely [4]. Node.js does not reliably emit an 'error' event specifically for client-side aborts unless manually handled in the application logic [5].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

node <<'JS'
const http = require("node:http");
const { EventEmitter } = require("node:events");
const { Readable } = require("node:stream");

const req = http.request({ host: "127.0.0.1", port: 1 });
const result = {
  node: process.version,
  clientRequestSetTimeout: typeof req.setTimeout,
  clientRequestDestroy: typeof req.destroy,
  readableSetTimeout: typeof Readable.prototype.setTimeout,
  eventEmitterMethods: {
    on: typeof EventEmitter.prototype.on,
    once: typeof EventEmitter.prototype.once,
  },
};
req.once("error", () => {});
req.destroy();
console.log(JSON.stringify(result, null, 2));
JS

printf '%s\n' '--- exact CI Node declarations ---'
rg -n -C 2 'node-version|setup-node|engines' .github package.json | head -80

Repository: gitcommit90/1Helm

Length of output: 771


🌐 Web query:

site:nodejs.org/docs/v22.14.0/api/http.html IncomingMessage message.setTimeout ClientRequest setTimeout aborted close

💡 Result:

In Node.js, the timeout behaviors for HTTP objects are distinct between ClientRequest and IncomingMessage. ClientRequest represents the request made by a client. It provides a request.setTimeout(timeout[, callback]) method which, once a socket is assigned and connected, calls socket.setTimeout on the underlying socket [1]. The 'timeout' event is emitted when the underlying socket times out from inactivity; notably, this event only notifies that the socket has been idle, and the request must be destroyed manually by the developer [1]. IncomingMessage represents the response object received by the client (or the request object received by a server). While the http.ServerResponse and some socket-based streams may support specific timeout methods, IncomingMessage itself does not provide a general message.setTimeout method in the documented HTTP API [1]. If you are encountering errors involving aborted or close events in relation to timeouts, they typically follow this lifecycle: 1. Timeout Trigger: The socket times out due to inactivity [1]. 2. Manual Intervention: Because the timeout event only notifies of idleness, you must explicitly call request.destroy or request.abort to close the connection [1]. 3. Event Emission: Once destroyed or aborted, the 'abort' and 'close' events are emitted on the request object [1]. If you are observing unexpected 'aborted' or 'close' behavior, ensure your code explicitly handles the 'timeout' event by destroying the request if the default idle timeout behavior is not desired [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

node <<'JS'
const http = require("node:http");
const { EventEmitter } = require("node:events");

const req = http.request({ host: "127.0.0.1", port: 1 });
const incoming = http.IncomingMessage.prototype;
const result = {
  node: process.version,
  clientRequest: {
    setTimeout: typeof req.setTimeout,
    destroy: typeof req.destroy,
  },
  incomingMessage: {
    setTimeout: typeof incoming.setTimeout,
    destroy: typeof incoming.destroy,
    on: typeof incoming.on,
    once: typeof incoming.once,
    abortedDescriptor: Object.getOwnPropertyDescriptor(incoming, "aborted") !== undefined,
  },
  eventEmitter: {
    on: typeof EventEmitter.prototype.on,
    once: typeof EventEmitter.prototype.once,
  },
};
req.once("error", () => {});
req.destroy();
console.log(JSON.stringify(result, null, 2));
JS

Repository: gitcommit90/1Helm

Length of output: 494


🌐 Web query:

Node.js v22.14.0 http IncomingMessage.prototype.setTimeout source message.setTimeout

💡 Result:

In Node.js v22.14.0, the message.setTimeout method is a member of the http.IncomingMessage prototype [1][2]. Its primary function is to set a timeout value on the underlying socket associated with the incoming message [3][4]. Method Definition The method signature is message.setTimeout(msecs[, callback]) [4]. It accepts two arguments: msecs (number): The number of milliseconds to wait before the socket times out. callback (function, optional): An optional listener to be called when a timeout occurs [4]. Implementation Details Internally, the method is defined in the Node.js source code (typically lib/_http_incoming.js) as follows [3][5]: IncomingMessage.prototype.setTimeout = function setTimeout(msecs, callback) { if (callback) this.on('timeout', callback); this.socket.setTimeout(msecs); return this; }; Behavioral Notes 1. Socket Delegation: The method calls the setTimeout method of the underlying message.socket [6][4]. 2. Return Value: It returns the IncomingMessage instance itself, allowing for method chaining [7]. 3. Callback Handling: While the implementation adds the callback as a listener for the 'timeout' event on the IncomingMessage instance, historical issues have noted that because IncomingMessage does not always emit 'timeout' directly, reliance on the callback via this specific method has been a point of technical discussion [3]. Users are often advised to interact directly with the socket's timeout mechanisms if precise control is required [3]. Documentation for this method can be found in the official Node.js HTTP module documentation under the IncomingMessage class section [1][2].

Citations:


Bound requestWithHost to a finite timeout.

Use req.setTimeout(...) and res.setTimeout(...). Destroy or reject from timeout handlers because timeouts emit events but do not reject the promise. Also reject on response error and aborted events. These APIs are available on the CI Node.js 22 target.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/site.mjs` around lines 14 - 21, Update requestWithHost to apply a finite
timeout via req.setTimeout and res.setTimeout, with each timeout handler
destroying the connection and rejecting the promise. Add response error and
aborted handlers that reject as well, while preserving the existing successful
end resolution.

Comment thread test/site.mjs
assert.match(gettingStarted, /On Windows 11 x64, download the Setup executable/i);
assert.match(gettingStarted, /v0\.0\.30 is <code>NotSigned<\/code>/i);
assert.match(gettingStarted, /connect to an existing HTTPS 1Helm host/i);
assert.doesNotMatch(gettingStarted, /signed Setup executable/i);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- test/site.mjs context ---'
sed -n '45,75p' test/site.mjs
printf '%s\n' '--- related wording and assertions ---'
rg -n -C 2 'signed Setup executable|unsigned Setup executable|doesNotMatch|getttingStarted|gettingStarted' test .
printf '%s\n' '--- regex behavior ---'
python3 - <<'PY'
import re
pattern = re.compile(r"signed Setup executable", re.I)
bounded = re.compile(r"\bsigned Setup executable\b", re.I)
for text in ("signed Setup executable", "unsigned Setup executable"):
    print(repr(text), bool(pattern.search(text)), bool(bounded.search(text)))
PY

Repository: gitcommit90/1Helm

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -eu
node - <<'JS'
const broad = /signed Setup executable/i;
const bounded = /\bsigned Setup executable\b/i;
for (const text of ["signed Setup executable", "unsigned Setup executable"]) {
  console.log(JSON.stringify(text), {
    broad: broad.test(text),
    bounded: bounded.test(text),
  });
}
JS

Repository: gitcommit90/1Helm

Length of output: 272


Use word boundaries in the negative assertion.

/signed Setup executable/i matches the phrase inside unsigned Setup executable. Use /\bsigned Setup executable\b/i to reject only the explicit wording.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/site.mjs` at line 63, Update the negative assertion for gettingStarted
to use word boundaries around “signed Setup executable,” so it rejects only the
explicit phrase and not “unsigned Setup executable.”

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant